package classfile

import (
	"fmt"
	"math"
)

type RuntimeVisibleAnnotationsAttr struct {
	AttrStruct
	annotationNumber uint16
	annotations      []Annotation
}
type Annotation struct {
	typeIndex uint16
	pairNum   uint16
	pairs     []Pair
}

type Pair struct {
	pairIndex uint16
	pairValue interface{}
}

func (self *RuntimeVisibleAnnotationsAttr) readAttrInfo(cp ConstantPool) AttributeInfo {

	self.annotationNumber = self.attributeData.ReadUInt16()

	if self.annotationNumber > 0 {
		self.annotations = make([]Annotation, self.annotationNumber)
		for i := 0; i < int(self.annotationNumber); i++ {
			self.annotations[i] = NewAnnotationInfo(&self.attributeData, cp)
		}
	}
	return self
}

func NewAnnotationInfo(data *AttributeData, cp ConstantPool) Annotation {
	a := &Annotation{}
	a.typeIndex = data.ReadUInt16()
	a.pairNum = data.ReadUInt16()
	if a.pairNum > 0 {
		a.pairs = make([]Pair, a.pairNum)
		for i := 0; i < len(a.pairs); i++ {
			a.pairs[i] = NewPair(data, cp)
		}
	}
	return *a
}

func NewPair(data *AttributeData, cp ConstantPool) Pair {
	p := &Pair{}
	p.pairIndex = data.ReadUInt16()
	p.pairValue = getValue(p.pairIndex, data)
	return *p
}

func getValue(typeIndex uint16, data *AttributeData) interface{} {
	switch typeIndex {
	case 0x0000: // BYTE
		return data.ReadUInt8()
	case 0x0001: // CHAR
		return rune(data.ReadUInt8())
	case 0x0002: // DOUBLE
		return math.Float64frombits(data.ReadUInt64())
	case 0x0003: // FLOAT
		return math.Float32frombits(data.ReadUInt32())
	case 0x0004: // INT
		return int32(data.ReadUInt32())
	case 0x0005: // LONG
		return data.ReadUInt64()
	case 0x0006: // SHORT
		return int16(data.ReadUInt16())
	case 0x0007: // TRUE_OR_FALSE (boolean)
		return (data.ReadUInt8()&0x01 != 0)
	default: // OTHER or PRIMITIVE_ Wrappers and Class are handled as interface{} with string name of the type in this example.
		return fmt.Sprintf("%v", string(data.GetPos())) // string conversion for other types (e.g., Strings) or wrappers for primitive types (e.g., Integer, Boolean).
	}
}
